(function($) {
	//G6 图形化渲染引擎官网：https://antv-g6.gitee.io/zh
	var G6Flow = {};
	window.G6Flow = G6Flow;

	/**
	 * 节点类型
	 * @param {Object} config 节点配置
	 */
	G6Flow.NodeType = function(config) {
		//内部维护对象
		var innerObject = {
			config: {
				preset: [{
					presetNodeTypeName: 'test',
					config: undefined
				}, {
					presetNodeTypeName: 'start'
				}, {
					presetNodeTypeName: 'operate'
				}, {
					presetNodeTypeName: 'nodeIf'
				}, {
					presetNodeTypeName: 'end'
				}]
			},
			/**
			 * 节点类型数据管理
			 * types对象的每一个字段等于一个节点类型的type值，而该节点的类型的数据由该type对应的对象保管；
			 * 示例，有一个类型为test的节点，其数据在types中的存放规则是
			 * 	types[test] = {
			 *		nodeTypeObject: new NodeType()
			 * 		, ..... 其它
			 *  }
			 */
			types: {}
		};
		var mainThis = this;
		/**
		 * 注册节点类型
		 * @param {Object} config 可配置参数
		 * config: {
		 *			type: '' , 节点类型
		 * 			sourceLineNumber: 4, 节点可出线数量
		 * 			targetLineNumber: 4, 节点可接线数量
		 * 			model: {}, 节点绘制样式和属性等，参考G6节点绘制model
		 * 			width: 100, html元素节点渲染宽度
		 * 			height: 30, html元素节点渲染高度
		 * 			renderFunction: function(element,width ,height ){}, html元素节点渲染方法;
		 * 			reRenderFunction: function(element,width ,height ){}, html元素重新渲染方法;
		 * },
		 */
		mainThis.registerType = function(config) {
			innerObject.types[config.type] = new NodeType(config);
		}
		/**
		 * 获取指定的节点
		 * @param {Object} type 节点类型
		 */
		mainThis.getType = function(type) {
			return innerObject.types[type];
		}
		/**
		 * 渲染指定的节点类型
		 * @param {Object} type 节点类型 ，当不指定时，渲染所有类型的节点
		 */
		mainThis.render = function(type) {
			if (type) {
				mainThis.getType(type) ? mainThis.getType(type).render() : '';
			} else {
				for (var item in innerObject.types) {
					innerObject.types[item].render();
				}
			}
		}

		//初始操作
		function init() {
			$.extend(innerObject.config, config);
			var presetNodeType = new PresetNodeType();
			for (var i = 0; i < innerObject.config.preset.length; ++i) {
				var preset = innerObject.config.preset[i];
				innerObject.types[preset.presetNodeTypeName] = presetNodeType.getNodeType(preset.presetNodeTypeName, preset.config);
			}
		}
		init();
		return mainThis;
	}

	//线条工具
	G6Flow.EdgeMenu = function() {
		var edgeMenu = new Menu('edge');
		//内置工具
		edgeMenu.appendMenu('line', '直线', function(g6flow, item) {
			g6flow.getG6Graph().updateItem(item, {
				type: 'line'
			});
		})
		edgeMenu.appendMenu('polyline', '折线', function(g6flow, item) {
			g6flow.getG6Graph().updateItem(item, {
				type: 'polyline'
			});
		})
		edgeMenu.appendMenu('cubic', '曲线', function(g6flow, item) {
			g6flow.getG6Graph().updateItem(item, {
				type: 'cubic'
			});
		})
		edgeMenu.appendMenu('delete', '删除', function(g6flow, item) {
			g6flow.getG6Graph().removeItem(item);
		})
		return edgeMenu;
	}

	//节点工具
	G6Flow.NodeMenu = function() {
		var nodeMenu = new Menu('node');
		//内置工具
		nodeMenu.appendMenu('delete', '删除', function(g6flow, item) {
			g6flow.getG6Graph().removeItem(item);
		})
		return nodeMenu;
	}

	//绘图工具
	G6Flow.CanvasMenu = function() {
		function undo(graph) {
			const undoStack = graph.getUndoStack();
			if (!undoStack || undoStack.length === 0) {
				return;
			}
			const currentData = undoStack.pop();
			if (currentData) {
				var action = currentData.action;
				var copy = {};
				$.extend(true, copy, currentData.data)
				graph.pushStack(action, copy, 'redo');
				var data = currentData.data.before;
				if (action === 'add') {
					data = currentData.data.after;
				}
				if (!data) return;
				switch (action) {
					case 'visible':
					{
						Object.keys(data).forEach((key) => {
							const array = data[key];
						if (!array) return;
						array.forEach((model) => {
							const item = graph.findById(model.id);
						if (model.visible) {
							graph.showItem(item, false);
						} else {
							graph.hideItem(item, false);
						}
					});
					});
						break;
					}
					case 'render':
					case 'update':
						Object.keys(data).forEach((key) => {
							const array = data[key];
						if (!array) return;
						array.forEach((model) => {
							graph.updateItem(model.id, model, false);
				});
			});
				break;
			case 'changedata':
				graph.changeData(data, false);
				break;
			case 'delete':
				{
					Object.keys(data).forEach((key) => {
						const array = data[key];
					if (!array) return;
					array.forEach((model) => {
						const itemType = model.itemType;
					delete model.itemType;
					graph.addItem(itemType, model, false);
				});
				});
					break;
				}
			case 'add':
				Object.keys(data).forEach((key) => {
					const array = data[key];
				if (!array) return;
				array.forEach((model) => {
					graph.removeItem(model.id, false);
			});
			});
				break;
			case 'updateComboTree':
				Object.keys(data).forEach((key) => {
					const array = data[key];
				if (!array) return;
				array.forEach((model) => {
					graph.updateComboTree(model.id, model.parentId, false);
			});
			});
				break;
			default:
			}
			}
		}

		function redo(graph) {
			const redoStack = graph.getRedoStack();
			if (!redoStack || redoStack.length === 0) {
				return;
			}
			const currentData = redoStack.pop();
			if (currentData) {
				const action = currentData.action;
				var data = currentData.data.after;
				var copy = {};
				$.extend(true, copy, currentData.data)
				graph.pushStack(action, copy);
				if (action === 'delete') {
					data = currentData.data.before;
				}
				if (!data) return;
				switch (action) {
					case 'visible':
					{
						Object.keys(data).forEach((key) => {
							const array = data[key];
						if (!array) return;
						array.forEach((model) => {
							const item = graph.findById(model.id);
						if (model.visible) {
							graph.showItem(item, false);
						} else {
							graph.hideItem(item, false);
						}
					});
					});
						break;
					}
					case 'render':
					case 'update':
						Object.keys(data).forEach((key) => {
							const array = data[key];
						if (!array) return;
						array.forEach((model) => {
							graph.updateItem(model.id, model, false);
				});
			});
				break;
			case 'changedata':
				graph.changeData(data, false);
				break;
			case 'delete':
				if (data.edges) {
					data.edges.forEach((model) => {
						graph.removeItem(model.id, false);
				});
				}
				if (data.nodes) {
					data.nodes.forEach((model) => {
						graph.removeItem(model.id, false);
				});
				}
				if (data.combos) {
					data.combos.forEach((model) => {
						graph.removeItem(model.id, false);
				});
				}
				break;
			case 'add':
				{
					Object.keys(data).forEach((key) => {
						const array = data[key];
					if (!array) return;
					array.forEach((model) => {
						const itemType = model.itemType;
					delete model.itemType;
					graph.addItem(itemType, model, false);
				});
				});
					break;
				}
			case 'updateComboTree':
				Object.keys(data).forEach((key) => {
					const array = data[key];
				if (!array) return;
				array.forEach((model) => {
					graph.updateComboTree(model.id, model.parentId, false);
			});
			});
				break;
			default:
			}
			}
		}

		var canvasMenu = new Menu('canvas');

		//内置工具
		canvasMenu.appendMenu('refresh', '刷新', function(g6flow, item) {
			try {
				g6flow.getG6Graph().refresh();
			} catch (e) {
				console.log(e);
				g6flow.getG6Graph().clearStack('刷新', e);
			}
		})
		canvasMenu.appendMenu('undo', '撤销', function(g6flow, item) {
			try {
				undo(g6flow.getG6Graph());
			} catch (e) {
				g6flow.getG6Graph().clearStack();
				console.log('撤销异常', e)
			}
		})
		canvasMenu.appendMenu('redo', '回显', function(g6flow, item) {
			try {
				redo(g6flow.getG6Graph());
			} catch (e) {
				console.log(e);
				g6flow.getG6Graph().clearStack('回显异常', e);
			}
		})
		canvasMenu.appendMenu('clear', '清空/新建', function(g6flow, item) {
			g6flow.getG6Graph().clear();
		})
		return canvasMenu;
	}


	//图形渲染
	G6Flow.Flow = function(g6config, plugins) {
		//内部维护对象
		var innerObject = {
			g6config: {
				modes: {
					default: [{
						type: 'drag-canvas',
						scalableRange: 500
					}, {
						type: 'click-select',
						trigger: 'ctrl'
					}, {
						type: 'activate-relations'
					}, {
						type: 'brush-select',
						trigger: 'ctrl'
					}, {
						type: 'drag-combo'
					}, 'zoom-canvas', 'drag-node', 'collapse-expand-combo'],
				},
				plugins: [new G6.Grid()]
			},
			plugins: {
				nodeType: new G6Flow.NodeType(),
				edgeMenu: new G6Flow.EdgeMenu(),
				nodeMenu: new G6Flow.NodeMenu(),
				canvasMenu: new G6Flow.CanvasMenu()
			},
			g6canvas: {},
			helper: {

			}
		}
		var mainThis = this;
		mainThis.getG6canvas = function() {
			return innerObject.g6canvas;
		}
		mainThis.getPlugins = function() {
			return innerObject.plugins;
		}
		mainThis.getG6Graph = function() {
			return innerObject.g6canvas.graph();
		}

		mainThis.getNodeTypeHelper = function(){
			return innerObject.helper.nodeType ? innerObject.helper.nodeType : innerObject.helper.nodeType = new  NodeTypeHelper(mainThis);
		}
		/**
		 * 注册时机事件处理
		 * @param {Object} timingEventName 时机事件类型，更多事件类型参考G6时机事件；可选：
		 * 	beforeadditem 在画板上创建节点之前调用
		 *  afteradditem 在画板上创建节点之后调用
		 *  beforeremoveitem 在画板上删除节点之前调用
		 *  afterremoveitem 在画板上删除节点之后调用
		 * @param {Object} call 回调，可用参数：nodeInfo:{id:'' ,type:'' ,custom:{}} 节点信息 ； timingEventName 事件类型
		 */
		mainThis.registerTimingEvent = function(timingEventName, call) {
			mainThis.getG6canvas().registerG6TimingEvent(timingEventName, function(evt, timingEventName) {
				try {
					call(evt, timingEventName);
				} catch (e) {
					console.log(e);
				}
			})
		}
		//初始
		function init() {
			$.extend(innerObject.g6config, g6config);
			//未设置宽高时，自动适配
			!innerObject.g6config.width && $('#' + innerObject.g6config.container).width() ? innerObject.g6config.width = $(
				'#' + innerObject.g6config.container).width() : '';
			!innerObject.g6config.height && $('#' + innerObject.g6config.container).height() ? innerObject.g6config.height = $(
				'#' + innerObject.g6config.container).height() : '';
			//节点连接线控制
			innerObject.g6config.modes.default.push({
				type: 'create-edge',
				trigger: 'click',
				key: 'shift',
				shouldBegin: function(e) {
					if (e.item._cfg.type != 'node') {
						return false;
					}
					var nodeType = e.item._cfg.model.custom.type;
					if (nodeType) {
						var nodeTypeObject = innerObject.plugins.nodeType.getType(nodeType);
						if (nodeTypeObject && nodeTypeObject.getSourceLineNumber() != undefined) {
							if (nodeTypeObject.getSourceLineNumber() <= 0) {
								return false;
							}
							if (e.item._cfg.edges) {
								var edges = e.item._cfg.edges;
								var currentSourceN = 0;
								for (var i = 0; i < edges.length; ++i) {
									if (edges[i]._cfg.model.source === e.item._cfg.id) {
										currentSourceN++;
									}
								}
								if (currentSourceN >= nodeTypeObject.getSourceLineNumber()) {
									return false;
								}
							}
						}
					}
					return true;
				},
				shouldEnd: function(e) {
					if (e.item._cfg.type != 'node') {
						return false;
					}
					var nodeType = e.item._cfg.model.custom.type;
					if (nodeType) {
						var nodeTypeObject = innerObject.plugins.nodeType.getType(nodeType);
						if (nodeTypeObject && nodeTypeObject.getTargetLineNumber() != undefined) {
							if (nodeTypeObject.getTargetLineNumber() <= 0) {
								return false;
							}
							if (e.item._cfg.edges) {
								var edges = e.item._cfg.edges;
								var currentTargetN = 0;
								for (var i = 0; i < edges.length; ++i) {
									if (edges[i]._cfg.model.target === e.item._cfg.id) {
										currentTargetN++;
									}
								}
								if (currentTargetN >= nodeTypeObject.getTargetLineNumber()) {
									return false;
								}
							}
						}
					}
					return true;
				}
			})

			//扩展插件
			$.extend(innerObject.plugins, plugins);
			innerObject.plugins.nodeType.render();
			innerObject.g6config.plugins.push(innerObject.plugins.edgeMenu.createMenu(mainThis));
			innerObject.g6config.plugins.push(innerObject.plugins.nodeMenu.createMenu(mainThis));
			innerObject.g6config.plugins.push(innerObject.plugins.canvasMenu.createMenu(mainThis));

			//实例化g6 图形绘制工具
			innerObject.g6canvas = new G6Canvas(innerObject.g6config);
			innerObject.g6canvas.render();

			//为绘制容器绑定拖拽节点绘制功能
			$('#' + innerObject.g6config.container).on('drop', function(ev) {
				ev.originalEvent.preventDefault();
				var point = innerObject.g6canvas.graph().getPointByClient(ev.originalEvent.clientX, ev.originalEvent.clientY);
				var nodeType = ev.originalEvent.dataTransfer.getData("nodeType");
				var nodeTypeId = ev.originalEvent.dataTransfer.getData("nodeTypeId");
				var nodeTypeObject = innerObject.plugins.nodeType.getType(nodeType);
				var model = {
					anchorPoints: [
						[0, 0],
						[0, 0.5],
						[0, 1],
						[0.5, 0],
						[0.5, 1],
						[1, 0.5],
						[1, 0],
						[1, 1]
					],
					x: point.x,
					y: point.y
				};
				$.extend(model, nodeTypeObject.getDrawNodeModel(nodeTypeId));
				innerObject.g6canvas.graph().addItem('node', model);
			}).on('dragover', function(ev) {
				ev.originalEvent.preventDefault();
			});
		}
		init();
		return mainThis;
	}

	/**
	 * 节点工具
	 * @param {Object} flow g6流程对象
	 */
	function NodeTypeHelper(g6flow){
		var mainThis = this;
		/**
		 * 注册事件处理
		 * @param {String} nodeType  节点类型
		 * @param {Object} eventType 事件类型，参考js 事件；示例： click ,dbclick ,mouseover ,mouseout
		 * @param {Object} call 回调，可用参数：nodeInfo:{id:'' ,type:'' ,custom:{}} 节点信息 ； eventType 事件类型
		 */
		mainThis.registerEvent = function(nodeType, eventType, call) {
			g6flow.getG6canvas().registerNodeEvent(eventType, function(item, eventType) {
				var model = item._cfg.model;
				if (model.custom && model.custom.type === nodeType) {
					try {
						var info = {
							id: model.id,
							type: model.custom.type,
							custom: model.custom
						}
						call(info, eventType);
					} catch (e) {
						console.log(e);
					}
				}
			});
		}
		return mainThis;
	}

	/**
	 * G6Canvas 工具
	 * @param {Object} config 参考G6图形化渲染引擎的配置信息
	 */
	function G6Canvas(config) {
		var innerObject = {
			config: {
				container: '',
				width: 500,
				height: 500,
				enabledStack: false,
				animate: false,
				comboStateStyles: {
					selected: {
						stroke: 'red',
						lineWidth: 3,
					},
				},
				nodeStateStyles: {
					selected: {
						fill: 'red'
					},
				},
				edgeStateStyles: {
					endArrow: true,
					lineWidth: 3,
					stroke: 'black',
					hover: {
						stroke: 'red',
						lineWidth: 6,
					}
				}
			},
			graph: {},
			timingEvent: {}
		};
		var mainThis = this;
		mainThis.graph = function() {
			return innerObject.graph;
		}
		mainThis.render = function() {
			mainThis.graph().render();
		}
		/**
		 * 绑定事件类型
		 * @param {Object} eventType 事件类型 ，参考G6节点事件类型 ，
		 * @param {Object} call 回调，可用参数： item 事件触发的节点 , eventType 事件类型
		 */
		mainThis.registerNodeEvent = function(eventType, call) {
			innerObject.graph.on('node:' + eventType, evt => {
				call(evt.item, eventType)
		});
		}
		/**
		 * 注册G6的时机事件，时机类型请参考G6 时机事件
		 * 部分示例：
		 *  beforerender	调用 graph.render / graph.read 方法之前触发
		 * 	afterrender	调用 graph.render / graph.read 方法之后触发
		 * 	beforeadditem	调用 graph.add / graph.addItem 方法之前触发
		 * 	afteradditem	调用 graph.add / graph.addItem 方法之后触发
		 * 	beforeremoveitem	调用 graph.remove / graph.removeItem 方法之前触发
		 * 	afterremoveitem	调用 graph.remove / graph.removeItem 方法之后触发
		 * 	beforeupdateitem	调用 graph.update / graph.updateItem 方法之前触发
		 * 	afterupdateitem	调用 graph.update / graph.updateItem 方法之后触发
		 * 	beforeitemvisibilitychange	调用 graph.showItem / graph.hideItem 方法之前触发
		 * 	afteritemvisibilitychange	调用 graph.showItem / graph.hideItem 方法之后触发
		 * 	beforeitemstatechange	调用 graph.setItemState 方法之前触发
		 * 	afteritemstatechange	调用 graph.setItemState 方法之后触发
		 * 	beforeitemrefresh	调用 graph.refreshItem 方法之前触发
		 * 	afteritemrefresh	调用 graph.refreshItem 方法之后触发
		 * @param {String} timingEventName 时机事件名称
		 * @param {Object} call 回调 ，可用参数： type 事件触发元素的类型 ; model 事件触发的元素的model值 ; timingEventName 事件类型
		 */
		mainThis.registerG6TimingEvent = function(timingEventName, call) {
			var timingEvent = innerObject.timingEvent[timingEventName];
			if (timingEvent) {
				timingEvent.calls.push(call);
			} else {
				timingEvent = innerObject.timingEvent[timingEventName] = {
					calls: [call]
				};
				innerObject.graph.on(timingEventName, evt => {
					for (var i = 0; i < timingEvent.calls.length; ++i) {
					timingEvent.calls[i](evt);
				}
			})
			}
		}

		function init() {
			$.extend(true, innerObject.config, config);
			innerObject.graph = new G6.Graph(innerObject.config);
			//绑定线条事件
			innerObject.graph.on('aftercreateedge', e => {
				e.edge._cfg.model.type = 'polyline';
			innerObject.graph.updateItem(e.edge, e.edge._cfg.model);
		})
			innerObject.graph.on('edge:mouseover', evt => {
				innerObject.graph.setItemState(evt.item, 'hover', true)
			});
			innerObject.graph.on('edge:mouseout', evt => {
				innerObject.graph.setItemState(evt.item, 'hover', false)
			});
		}
		init();
		return mainThis;

	}

	/**
	 * 节点类型
	 * @param {Object} config 可配置参数
	 * config: {
	 *          type: '' , 节点类型
	 * 			sourceLineNumber: 4, 节点可出线数量
	 * 			targetLineNumber: 4, 节点可接线数量
	 * 			model: {}, 节点绘制样式和属性等，参考G6节点绘制model
	 * 			width: 100, html元素节点渲染宽度
	 * 			height: 30, html元素节点渲染高度
	 * 			renderFunction: function(element,width ,height ){}, html元素节点渲染方法;
	 * 			reRenderFunction: function(element,width ,height ){}, html元素重新渲染方法;
	 * 		},
	 */
	function NodeType(config) {
		var mainThis = this;
		//内部维护对象
		var innerObject = {
			count: 0,
			config: {
				type: '',
				sourceLineNumber: 4,
				targetLineNumber: 4,
				model: {},
				width: 100,
				height: 30,
				renderFunction: undefined,
				reRenderFunction: undefined,
			},
			/**
			 * 自定义数据；数据来源是：节点类型对应的html元素的data属性设定的所有的值；
			 * 同一类型的节点，可能存在多个html元素，此时，使用html元素的cutom.idName属性所对应的值，进行个性化数据区分，
			 * 而html元素的cutom.idName属性的值，会在元素被渲染时进行动态设置；
			 */
			custom: {
				//相同类型，不同节点的个性差异id名称属性
				idName: 'g6flow-node-type-id',
				idCount: 0,
			}
		}
		//暴露内部数据
		/**
		 * 获取配置数据
		 */
		mainThis.getConfig = function() {
			return innerObject.config;
		}
		/**
		 * 获取绘制节点时需要的Model数据
		 */
		mainThis.getDrawNodeModel = function(nodeTypeId) {
			var nodeModel = {};
			$.extend(nodeModel, innerObject.config.model);
			nodeModel.id = innerObject.config.type + "_" + (new Date().getTime()) + "_" + (innerObject.count++);
			nodeModel.label = mainThis.getDrowNodeName(nodeTypeId);
			nodeModel.width ? '' : nodeModel.width = innerObject.config.width;
			nodeModel.height ? '' : nodeModel.height = innerObject.constructor.height;
			nodeModel.custom = {
				type: innerObject.config.type
			};
			innerObject.custom[nodeTypeId] ? $.extend(nodeModel.custom, innerObject.custom[nodeTypeId]) : '';
			return nodeModel;
		}
		/**
		 * 获取绘制节点的展示的名称
		 */
		mainThis.getDrowNodeName = function(nodeTypeId) {
			return innerObject.custom[nodeTypeId] && innerObject.custom[nodeTypeId].value ? innerObject.custom[nodeTypeId].value :
				innerObject.config.type;
		}
		/**
		 * 获取节点可出线的数量
		 */
		mainThis.getSourceLineNumber = function() {
			return config.sourceLineNumber;
		}
		/**
		 * 获取节点可接线的数量
		 */
		mainThis.getTargetLineNumber = function() {
			return config.targetLineNumber;
		}
		/**
		 * 获取当前节点类型的html元素
		 */
		mainThis.getElements = function() {
			var elements = new Array();
			$('.node').filter('.' + innerObject.config.type + ',[type="' + innerObject.config.type + '"]').each(function() {
				elements.push(this);
			})
			return elements;
		}
		/**
		 * 渲染节点
		 */
		mainThis.render = function() {
			//清空个性化数据
			for (var item in innerObject.custom) {
				if (item !== 'idName' && item !== 'idCount') {
					delete innerObject.custom[item];
				}
			}
			innerObject.custom.idCount = 0;

			//渲染
			$(mainThis.getElements()).each(function() {
				initCustomData(this);
				renderStyle(this);
				buildEvent(this);
			})
		}

		function renderStyle(element) {
			var width = innerObject.config.width;
			var height = innerObject.config.height;
			if ($(element).hasClass('g6flow-rendered')) {
				//再次渲染
				if (!innerObject.config.reRenderFunction || innerObject.config.reRenderFunction === '') {
					//不做处理
				} else {
					innerObject.config.reRenderFunction(element, width, height);
				}
			} else {
				//首次渲染
				if (!innerObject.config.renderFunction || innerObject.config.renderFunction === '') {
					var nodeTypeId = $(element).attr(innerObject.custom.idName);
					$(element).css({
						width: width + 'px',
						height: height + 'px',
						'line-height': height + 'px'
					}).append('<span>' + mainThis.getDrowNodeName(nodeTypeId) + '</span>');
				} else {
					innerObject.config.renderFunction(element, width, height);
				}
			}
			$(element).addClass('g6flow-rendered');
		}

		function buildEvent(element) {
			//方法绑定
			$(element).attr('draggable', 'true')
			$(element).unbind('dragstart').on('dragstart', function(ev) {
				ev.originalEvent.dataTransfer.setData("nodeType", innerObject.config.type);
				ev.originalEvent.dataTransfer.setData("nodeTypeId", $(element).attr(innerObject.custom.idName));
			})
		}

		function initCustomData(element) {
			var id = $(element).attr(innerObject.custom.idName);
			!id ? id = innerObject.custom.idName + '_' + (new Date().getTime() + '_' + (++innerObject.custom.idCount)) : ''
			$(element).attr(innerObject.custom.idName, id);
			innerObject.custom[id] = {};
			$.extend(innerObject.custom[id], $(element).data());
			innerObject.custom[id].type = innerObject.config.type;
		}

		function init() {
			$.extend(innerObject.config, config);
		}
		init();
		return mainThis;
	}

	/**
	 * 预置类型
	 */
	function PresetNodeType() {
		var width = 100,
			height = 30;
		var preset = {
			test: {
				type: 'test',
				width: width,
				height: height,
				model: {
					label: 'test',
					type: 'rect'
				}
			},
			start: {
				type: 'start',
				model: {
					width: width,
					height: height,
					label: 'start',
					type: 'rect',
					style: {
						stroke: 'green',
						radius: 20,
					}
				},
				sourceLineNumber: 1,
				targetLineNumber: 0,
				renderFunction: function(element, width, height) {
					var showName = $(element).data('value') ? $(element).data('value') : def.type;
					$(element).css({
						width: width + 'px',
						height: height + 'px',
						'line-height': height + 'px',
						border: '1px solid green',
						'border-radius': (width / 2) + 'px',
					}).append('<span>' + showName + '</span>');
				}
			},
			operate: {
				type: 'operate',
				width: width,
				height: height,
				model: {
					label: 'operate',
					type: 'rect'
				},
				sourceLineNumber: 1,
				targetLineNumber: 9
			},
			nodeIf: {
				type: 'nodeIf',
				model: {
					label: 'nodeIf',
					type: 'diamond'
				},
				width: 80,
				height: 80,
				sourceLineNumber: 2,
				targetLineNumber: 9,
				renderFunction: function(element, width, height) {
					var showName = $(element).data('value') ? $(element).data('value') : def.type;
					$(element).css({
						width: (width / 2) + 'px',
						height: (width / 2) + 'px',
						'line-height': (width / 2) + 'px',
						border: '1px solid #409EFF',
						transform: 'rotate(45deg)',
						margin: (width / 4) + 'px ' + '0px ' + (width / 4) + 'px ' + (width / 2) + 'px '
					}).append('<span>' + showName + '</span>');
					$(element).children().css({
						display: 'inline-block',
						'max-width': '100%',
						transform: 'rotate(-45deg)',
						overflow: 'hidden'
					})
				}
			},
			end: {
				type: 'end',
				width: width,
				height: height,
				model: {
					label: 'end',
					type: 'rect',
					style: {
						stroke: 'red',
						radius: 20,
					}
				},
				sourceLineNumber: 0,
				targetLineNumber: 9,
				styleFn: function(element, width, height) {
					var showName = $(element).data('value') ? $(element).data('value') : def.type;
					$(element).css({
						width: width + 'px',
						height: height + 'px',
						'line-height': height + 'px',
						border: '1px solid red',
						'border-radius': (width / 2) + 'px',
					}).append('<span>' + showName + '</span>');
				}
			}
		}
		var mainThis = this;
		/**
		 * 获取预置类型
		 * @param {Object} presetNodeTypeName 预置节点类型
		 * @param {Object} config 配置数据
		 */
		mainThis.getNodeType = function(presetNodeTypeName, config) {
			var presetNodeTypeConfig = preset[presetNodeTypeName];
			if (presetNodeTypeConfig) {
				var t = {}
				$.extend(t, presetNodeTypeConfig);
				if (config) {
					$.extend(t, config);
				}
				return new NodeType(t);
			}
		}
		return mainThis;
	}

	/**
	 * 菜单
	 * @param {String} type 菜单类型，可选： node | edge | canvas
	 */
	function Menu(type) {
		var innerObject = {
			$menuHomeHtml: $('<div class="menu-home"></div>'),
			type: type,
			/**
			 * 菜单个性化数据
			 * 数据映射关系为menuId ->{}
			 */
			menu: {

			}
		}
		var mainThis = this;
		//暴露内部数据
		mainThis.appendMenu = function(menuId, menuName, call) {
			innerObject.$menuHomeHtml.append('<div class="menu" data-id="' + menuId + '">' + menuName + '</div>');
			innerObject.menu[menuId] = {
				menuName: menuName,
				call: call
			}
		}
		/**
		 * 创建菜单
		 */
		mainThis.createMenu = function(g6flow) {
			return new G6.Menu({
				offsetX: 6,
				offsetX: 10,
				itemTypes: [innerObject.type],
				getContent(e) {
					return innerObject.$menuHomeHtml[0];
				},
				handleMenuClick(target, item) {
					var menu = innerObject.menu[$(target).data('id')];
					if (menu && menu.call) {
						menu.call(g6flow, item);
					}
				},
			});
		}
		return mainThis;
	}


})(jQuery);
